package org.fast.easy.framework.cloud.interceptor;



import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import org.fast.easy.core.constant.SecurityConstants;
import org.fast.easy.core.util.ServletUtils;
import org.fast.easy.core.util.StringUtils;
import org.fast.easy.common.ip.util.IpUtils;
import org.springframework.stereotype.Component;
import feign.RequestInterceptor;
import feign.RequestTemplate;

/**
 * feign 请求拦截器
 * Feign发送Get请求时，采用POJO传递参数 Method Not Allowed 405
 * （在 HttpURLConnection 中发现的，jdk原生的http连接请求工具类，原来是因为Feign默认使用的连接工具实现类，所以里面发现只要你有body体对象，就会强制的把get请求转换成POST请求。）
 * 解决办法：https://blog.csdn.net/echizao1839/article/details/128703362
 * fast-easy-framework-cloud
 * @author YanMing
 * @date 2024/5/16
 */
@Component
public class FeignRequestInterceptor implements RequestInterceptor
{
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    @Override
    public void apply(RequestTemplate requestTemplate)
    {
        HttpServletRequest httpServletRequest = ServletUtils.getRequest();
        if (StringUtils.isNotNull(httpServletRequest))
        {
            Map<String, String> headers = ServletUtils.getHeaders(httpServletRequest);
            // 传递用户信息请求头，防止丢失
            String userId = headers.get(SecurityConstants.DETAILS_USER_ID);
            if (StringUtils.isNotEmpty(userId))
            {
                requestTemplate.header(SecurityConstants.DETAILS_USER_ID, userId);
            }
            String userKey = headers.get(SecurityConstants.USER_KEY);
            if (StringUtils.isNotEmpty(userKey))
            {
                requestTemplate.header(SecurityConstants.USER_KEY, userKey);
            }
            String userName = headers.get(SecurityConstants.DETAILS_USERNAME);
            if (StringUtils.isNotEmpty(userName))
            {
                requestTemplate.header(SecurityConstants.DETAILS_USERNAME, userName);
            }
            String authentication = headers.get(SecurityConstants.AUTHORIZATION_HEADER);
            if (StringUtils.isNotEmpty(authentication))
            {
                requestTemplate.header(SecurityConstants.AUTHORIZATION_HEADER, authentication);
            }

            // 配置客户端IP
            requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr());
        }

        // feign 不支持 GET 方法传 POJO, json body转query
        if (requestTemplate.method()!= null && requestTemplate.body() != null) {
            if ("GET".equalsIgnoreCase(requestTemplate.method()) ) {
                try {
                    byte[] dataBytes = requestTemplate.body();
                    if (dataBytes != null && dataBytes.length > 0) {
                        JsonNode jsonNode = OBJECT_MAPPER.readTree(dataBytes);
                        requestTemplate.body(null, StandardCharsets.UTF_8);
                        // requestTemplate.body(Request.Body.empty());
                        Map<String, Collection<String>> queries = new HashMap<>();
                        buildQuery(jsonNode, "", queries);
                        requestTemplate.queries(queries);
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
        // 叶子节点
        if (!jsonNode.isContainerNode()) {
            if (jsonNode.isNull()) {
                return;
            }
            Collection<String> values = queries.computeIfAbsent(path, k -> new ArrayList<>());
            values.add(jsonNode.asText());
            return;
        }
        // 数组节点
        if (jsonNode.isArray()) {
            Iterator<JsonNode> it = jsonNode.elements();
            while (it.hasNext()) {
                buildQuery(it.next(), path, queries);
            }
        } else {
            Iterator<Map.Entry<String, JsonNode>> it = jsonNode.fields();
            while (it.hasNext()) {
                Map.Entry<String, JsonNode> entry = it.next();
                if (org.springframework.util.StringUtils.hasText(path)) {
                    buildQuery(entry.getValue(), path + "." + entry.getKey(), queries);
                } else {  // 根节点
                    buildQuery(entry.getValue(), entry.getKey(), queries);
                }
            }
        }
    }

}
